package me.zhengjie.log.aop;

import me.zhengjie.log.dao.LogDao;
import me.zhengjie.log.service.LogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;

@Component
public class WebLogAnnotationScanner extends WebLogBase implements ApplicationListener<ContextRefreshedEvent> {

    private final ApplicationContext applicationContext;

    private final Map<String, WebLogMetadata> webLogMetadataMap = new ConcurrentHashMap<>();

    private static final int BATCH_SIZE = 1;

    private static final int QUEUE_CAPACITY = 100;

    @Resource
    private LogService logService;

    private static final BlockingQueue<LogDao> queue = new ArrayBlockingQueue<>(QUEUE_CAPACITY);


    @Autowired
    public WebLogAnnotationScanner(ApplicationContext applicationContext, LogService logService) {
        this.applicationContext = applicationContext;
        handleLogWriteData();
    }

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        // 防止重复扫描（父子容器问题）
        if (event.getApplicationContext().getParent() == null) {
            scanWebLogAnnotations();
        }
    }

    private void scanWebLogAnnotations() {
        // 获取所有 Bean 名称
        String[] beanNames = applicationContext.getBeanDefinitionNames();

        for (String beanName : beanNames) {
            Object bean = applicationContext.getBean(beanName);
            Class<?> beanClass = bean.getClass();

            // 处理 CGLIB 代理类，获取原始类
            if (beanClass.getName().contains("$$EnhancerBySpringCGLIB$$")) {
                beanClass = beanClass.getSuperclass();
            }

            // 遍历类的所有方法
            for (Method method : beanClass.getDeclaredMethods()) {
                WebLog webLogAnnotation = AnnotationUtils.findAnnotation(method, WebLog.class);
                if (webLogAnnotation != null) {
                    // 存储注解元数据
                    WebLogMetadata metadata = new WebLogMetadata(
                            beanClass,
                            method,
                            webLogAnnotation.description(),
                            webLogAnnotation.logName(),
                            webLogAnnotation.category(),
                            webLogAnnotation.enterData()
                    );
                    // 使用类名+方法名作为唯一键
                    String key = this.getMethodName(beanClass, method);
                    webLogMetadataMap.put(key, metadata);
                }
            }
        }
    }

    // 提供查询方法
    public List<WebLogMetadata> getAllWebLogMetadata() {
        return new ArrayList<>(webLogMetadataMap.values());
    }

    public WebLogMetadata getWebLogMetadata(String className, String methodName) {
        String key = className + "#" + methodName;
        return webLogMetadataMap.get(key);
    }

    public WebLogMetadata getWebLogMetadata(String methodUnionId) {
        return webLogMetadataMap.get(methodUnionId);
    }


    public void handleLogWriteData() {
        new Thread((() -> {
            while (true) {
                try {
                    long start = System.currentTimeMillis();
                    while (queue.size() >= BATCH_SIZE) {
                        List<LogDao> batch = new ArrayList<>(queue.size());
                        // 最多从队列中取出BATCH_SIZE个元素
                        queue.drainTo(batch, queue.size());
                        logService.saveBatch(batch);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        })).start();


    }

    public static void addLog(LogDao logDao) {
        try {
            queue.put(logDao);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}